
#include <unistd.h>
#include <dlfcn.h>
#include <errno.h>

#include "hook.h"
#include "fiber.h"
#include "iomanager.h"
#include "macro.h"
#include "log.h"
#include "fd_manager.h"
#include "config.h"

static johnsonli::Logger::ptr g_logger = LOG_NAME("system");

namespace johnsonli
{
   static johnsonli::ConfigVar<int>::ptr g_tcp_connect_timeout =
        johnsonli::Config::Lookup("tcp.connect.timeout", 5000, "tcp connect timeout");
    static thread_local bool t_hook_enable = false;
    static uint64_t s_connect_timeout = -1;

    //hook列表
    #define HOOK_FUN(XX) \
        XX(sleep) \
        XX(usleep) \
        XX(nanosleep) \
        XX(socket) \
        XX(connect) \
        XX(accept) \
        XX(read) \
        XX(readv) \
        XX(recv) \
        XX(recvfrom) \
        XX(recvmsg) \
        XX(write) \
        XX(writev) \
        XX(send) \
        XX(sendto) \
        XX(sendmsg) \
        XX(close) \
        XX(fcntl) \
        XX(ioctl) \
        XX(getsockopt) \
        XX(setsockopt)

    void hook_init()
    {
        static bool is_inited = false;
        if(is_inited)
        {
            return;
        }

        //##连接name和_f，如果name是sleep --> sleep_f = (sleep_fun)dlsym(RTLD_NEXT, sleep);
        #define XX(name) name##_f = (name##_fun)dlsym(RTLD_NEXT, #name);

        //把[XX(sleep), XX(usleep)...]都展开成 sleep_f = (sleep_fun)dlsym(RTLD_NEXT, sleep);
        //这样sleep_f就和系统调用的sleep一样了
        HOOK_FUN(XX);   
        #undef XX
    }

    struct _HookIniter
    {
        _HookIniter()
        {
            hook_init();
            
            s_connect_timeout = g_tcp_connect_timeout->getValue();

            g_tcp_connect_timeout->addListener([](const int& old_value, const int& new_value){
                    LOG_INFO(g_logger) << "tcp connect timeout changed from "
                                            << old_value << " to " << new_value;
                    s_connect_timeout = new_value;
            });
        }
    };
    
    static _HookIniter s_hook_initer;   //全局静态变量，在main函数之前初始化，这样就会调用构造函数，保证在main开始前进行hook初始化

    bool is_hook_enable() {
        return t_hook_enable;
    }

    void set_hook_enable(bool flag) {
        t_hook_enable = flag;
    }

}

    //条件
    struct timer_info
    {
        int cancelled = 0;
    };

    //进程io操作的模板类
    template<typename OriginFun, typename... Args>
    static ssize_t do_io(int fd, OriginFun fun, const char* hook_fun_name
                            , uint32_t event, int timeout_so, Args&&... args)
    {
        //不使用hook，使用原来的系统调用
        if(!johnsonli::t_hook_enable)
        {
            return fun(fd, std::forward<Args>(args)...);
        }

        
        johnsonli::FdCtx::ptr ctx = johnsonli::FdMgr::GetInstance()->get(fd);
        if(!ctx)    //ctx = null
        {
            //LOG_INFO(g_logger) << "!ctx" << fd;
            return fun(fd, std::forward<Args>(args)...);
        }

        //fd关闭
        if(ctx->isClose())
        {
            LOG_INFO(g_logger) << "ctx->isClose()";
            errno = EBADF;
            return -1;
        }

        //不是socket，或者用户设置了非阻塞
        if(!ctx->isSocket() || ctx->getUserNonblock())
        {
            return fun(fd, std::forward<Args>(args)...);
        }

        uint64_t to = ctx->getTimeout(timeout_so);

        std::shared_ptr<timer_info> tinfo(new timer_info);
        
    retry:
        //先读一次，
        ssize_t n = fun(fd, std::forward<Args>(args)...);
        //中断就再试一次
        while(n == -1 && errno == EINTR)
        {
            n = fun(fd, std::forward<Args>(args)...);
        }

        //没数据，阻塞了，就需要开始设置异步了
        if(n == -1 && errno == EAGAIN)
        {
            johnsonli::IOManager* iom = johnsonli::IOManager::GetThis();
            johnsonli::Timer::ptr timer;
            std::weak_ptr<timer_info> winfo(tinfo);

            //有设置超时事件，就添加定时器任务
            if(to != (uint64_t)-1)
            {
//                LOG_INFO(g_logger) << "addConditionTimer: " << to/60;
                //定时器任务，当epoll_wait等待to毫秒后，说明fd注册的事件没有触发，就设置ETIMEDOUT，取消事件
                timer = iom->addConditionTimer(to, [winfo, fd, iom, event](){
                    auto t = winfo.lock();
                    if(!t || t->cancelled)
                    {
                        return;
                    }
                    //定时器触发，说明这个fd上的事件监听超时了，需要取消事件
                    t->cancelled = ETIMEDOUT;
                    iom->cancelEvent(fd, (johnsonli::IOManager::Event)(event));
                
                }, winfo);
            }

            //添加监听事件，任务处理就是当前协程
            int rt = iom->addEvent(fd, (johnsonli::IOManager::Event)(event));

//            LOG_INFO(g_logger) << "addEvent";

            //rt != 0，添加失败
            if(rt)
            {   
                LOG_ERROR(g_logger) << hook_fun_name << " addEvent("
                    << fd << ", " << event << ")";
                //失败要取消这个定时器
                if(timer)
                {
                    timer->cancel();
                }

                //LOG_INFO(g_logger) << "ADD EVENT : ";
                return -1;
            }
            else
            {
                johnsonli::Fiber::YieldToHoldBySwap();
//                LOG_INFO(g_logger) << "YieldToHoldBySwap ";
                if(timer)
                {
                    timer->cancel();
                }

                if(tinfo->cancelled)
                {
//                    LOG_INFO(g_logger) << "TIME OUT : ";
                    errno = tinfo->cancelled;
                    return -1;
                }

                //触发事件，继续读
                goto retry;
            }
        }

        //正常进行了io操作
//        LOG_INFO(g_logger) << "n : " << n;
        return n;
        
    }
    

 extern "C"
{
    //初始化[XX(sleep), XX(usleep)...] 都展开为 sleep_fun sleep_f = nullptr;
    #define XX(name) name ## _fun name ## _f = nullptr;
        HOOK_FUN(XX);
    #undef XX

    unsigned int sleep(unsigned int seconds) 
    {
        //如果没使用hook，就调用原系统函数
        if(!johnsonli::t_hook_enable)
        {
            return sleep_f(seconds);
        }

        //使用了hook，用自己实现的
        johnsonli::Fiber::ptr fiber = johnsonli::Fiber::GetThis();      //获取当前协程
        johnsonli::IOManager* iom = johnsonli::IOManager::GetThis();    //获取当前IOManager

        iom->addTimer(seconds * 1000, [iom, fiber](){
            iom->schedule(fiber);
        });

        johnsonli::Fiber::YieldToHoldBySwap();
        return 0;
    }

    int usleep(useconds_t usec) 
    {
        //如果没使用hook，就调用原系统函数
        if(!johnsonli::t_hook_enable)
        {
            return usleep_f(usec);
        }

        //使用了hook，用自己实现的
        johnsonli::Fiber::ptr fiber = johnsonli::Fiber::GetThis();      //获取当前协程
        johnsonli::IOManager* iom = johnsonli::IOManager::GetThis();    //获取当前IOManager

        iom->addTimer(usec / 1000, [iom, fiber](){
            iom->schedule(fiber);
        });

        johnsonli::Fiber::YieldToHoldBySwap();
        return 0;
    }

    int nanosleep(const struct timespec *req, struct timespec *rem)
    {
        if(!johnsonli::t_hook_enable)
        {
            return nanosleep_f(req, rem);
        }

        int timeout_ms = req->tv_sec * 1000 + req->tv_nsec / 1000 /1000;
        johnsonli::Fiber::ptr fiber = johnsonli::Fiber::GetThis();
        johnsonli::IOManager* iom = johnsonli::IOManager::GetThis();

        iom->addTimer(timeout_ms, [iom, fiber]()
        {
            iom->schedule(fiber);
        });

        johnsonli::Fiber::YieldToHoldBySwap();
        return 0;
    }
    
    int socket(int domain, int type, int protocol)
    {
        //不使用hook
        if(!johnsonli::t_hook_enable)
        {
            //LOG_INFO(g_logger) << "socket";
            return socket_f(domain, type, protocol);
        }

        //出错，直接返回
        int fd = socket_f(domain, type, protocol);
        if(fd == -1)
        {
            return fd;
        }

        //成功创建，需要存储相关信息
        johnsonli::FdMgr::GetInstance()->get(fd, true);
        //LOG_INFO(g_logger) << "socket timeout: " <<  johnsonli::FdMgr::GetInstance()->get(fd)->getTimeout(SO_RCVTIMEO);
        return fd;
    }

    int accept(int s, struct sockaddr *addr, socklen_t *addrlen)
    {
        int fd = do_io(s, accept_f, "accept", johnsonli::IOManager::READ, SO_RCVTIMEO, addr, addrlen);
        if(fd >= 0)
        {
            //成功创建，需要存储相关信息
            johnsonli::FdMgr::GetInstance()->get(fd, true);
        }

        return fd;
    }

    ssize_t read(int fd, void *buf, size_t count) 
    {
        return do_io(fd, read_f, "read", johnsonli::IOManager::READ, SO_RCVTIMEO, buf, count);
    }

    ssize_t readv(int fd, const struct iovec *iov, int iovcnt) 
    {
        return do_io(fd, readv_f, "readv", johnsonli::IOManager::READ, SO_RCVTIMEO, iov, iovcnt);
    }

    ssize_t recv(int sockfd, void *buf, size_t len, int flags) 
    {
        return do_io(sockfd, recv_f, "recv", johnsonli::IOManager::READ, SO_RCVTIMEO, buf, len, flags);
    }

    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) 
    {
        return do_io(sockfd, recvfrom_f, "recvfrom", johnsonli::IOManager::READ, SO_RCVTIMEO, buf, len, flags, src_addr, addrlen);
    }

    ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) 
    {
        return do_io(sockfd, recvmsg_f, "recvmsg", johnsonli::IOManager::READ, SO_RCVTIMEO, msg, flags);
    }

    ssize_t write(int fd, const void *buf, size_t count) 
    {
        return do_io(fd, write_f, "write", johnsonli::IOManager::WRITE, SO_SNDTIMEO, buf, count);
    }

    ssize_t writev(int fd, const struct iovec *iov, int iovcnt) 
    {
        return do_io(fd, writev_f, "writev", johnsonli::IOManager::WRITE, SO_SNDTIMEO, iov, iovcnt);
    }

    ssize_t send(int s, const void *msg, size_t len, int flags) 
    {
        return do_io(s, send_f, "send", johnsonli::IOManager::WRITE, SO_SNDTIMEO, msg, len, flags);
    }

    ssize_t sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) 
    {
        return do_io(s, sendto_f, "sendto", johnsonli::IOManager::WRITE, SO_SNDTIMEO, msg, len, flags, to, tolen);
    }

    ssize_t sendmsg(int s, const struct msghdr *msg, int flags) 
    {
        return do_io(s, sendmsg_f, "sendmsg", johnsonli::IOManager::WRITE, SO_SNDTIMEO, msg, flags);
    }

    int close(int fd)
    {
        if(!johnsonli::t_hook_enable)
        {
            return close_f(fd);
        }

        //需要hook，取消上面的所有事件，并触发事件处理函数
        johnsonli::FdCtx::ptr ctx = johnsonli::FdMgr::GetInstance()->get(fd);
        if(ctx)
        {
            auto iom = johnsonli::IOManager::GetThis();
            if(iom)
            {
                iom->cancelAll(fd);
            }

            johnsonli::FdMgr::GetInstance()->del(fd);
        }

        return close_f(fd);
    }

    int fcntl(int fd, int cmd, ... /* arg */ ) 
    {
        va_list va;
        va_start(va, cmd);
        switch (cmd)
        {
            case F_SETFL:
                {
                    int arg = va_arg(va, int);
                    va_end(va);
                    johnsonli::FdCtx::ptr ctx = johnsonli::FdMgr::GetInstance()->get(fd);
                    //普通文件，没有，关闭
                    if(!ctx || ctx->isClose() || !ctx->isSocket()) {
                        return fcntl_f(fd, cmd, arg);
                    }

                    //arg有没有设置非阻塞
                    ctx->setUserNonblock(arg & O_NONBLOCK);

                    //如果系统有非阻塞
                    if(ctx->getSysNonblock())
                    {
                        arg |= O_NONBLOCK;
                    }
                    else
                    {
                        arg &= ~O_NONBLOCK;
                    }
                    return fcntl_f(fd, cmd, arg);
                }
                break;
            case F_GETFL:
                {
                    va_end(va);
                    int arg = fcntl_f(fd, cmd);
                    johnsonli::FdCtx::ptr ctx = johnsonli::FdMgr::GetInstance()->get(fd);
                    if(!ctx || ctx->isClose() || !ctx->isSocket()) 
                    {
                        return arg;
                    }
                    if(ctx->getUserNonblock()) 
                    {
                        return arg | O_NONBLOCK;
                    } 
                    else 
                    {
                        return arg & ~O_NONBLOCK;
                    }
                }
                break;

            case F_DUPFD:
            case F_DUPFD_CLOEXEC:
            case F_SETFD:
            case F_SETOWN:
            case F_SETSIG:
            case F_SETLEASE:
            case F_NOTIFY:
#ifdef F_SETPIPE_SZ
            case F_SETPIPE_SZ:
#endif  
                {
                    int arg = va_arg(va, int);
                    va_end(va);
                    return fcntl_f(fd, cmd, arg);
                }
                break;
            case F_GETFD:
            case F_GETOWN:
            case F_GETSIG:
            case F_GETLEASE:
#ifdef F_GETPIPE_SZ
            case F_GETPIPE_SZ:
#endif
                {
                    va_end(va);
                    return fcntl_f(fd, cmd);
                }
                break;
            case F_SETLK:
            case F_SETLKW:
            case F_GETLK:
                {
                    struct flock* arg = va_arg(va, struct flock*);
                    va_end(va);
                    return fcntl_f(fd, cmd, arg);
                }
                break;
            case F_GETOWN_EX:
            case F_SETOWN_EX:
                {
                    struct f_owner_exlock* arg = va_arg(va, struct f_owner_exlock*);
                    va_end(va);
                    return fcntl_f(fd, cmd, arg);
                }
                break;
            default:
            va_end(va);
                return fcntl_f(fd, cmd);
        }
    }
    
    int connect_with_timeout(int fd, const struct sockaddr* addr, socklen_t addrlen, uint64_t timeout_ms) 
    {
        if(!johnsonli::t_hook_enable) {
            //LOG_INFO(g_logger) << "connect";
            return connect_f(fd, addr, addrlen);
        }

        johnsonli::FdCtx::ptr ctx = johnsonli::FdMgr::GetInstance()->get(fd);

        //不存在
        if(!ctx || ctx->isClose()) {
            errno = EBADF;
            return -1;
        }
        
        //不是socket
        if(!ctx->isSocket()) {
            return connect_f(fd, addr, addrlen);
        }

        //用户已经设置了非阻塞
        if(ctx->getUserNonblock()) {
            return connect_f(fd, addr, addrlen);
        }

        int n = connect_f(fd, addr, addrlen);
        if(n == 0)
        {
            //连接成功
            return 0;
        }
        else if(n != -1 || errno != EINPROGRESS)
        {
            return n;
        }

        johnsonli::IOManager* iom = johnsonli::IOManager::GetThis();
        johnsonli::Timer::ptr timer;
        std::shared_ptr<timer_info> tinfo(new timer_info);
        std::weak_ptr<timer_info> winfo(tinfo);

        if(timeout_ms != (uint64_t)-1)
        {
            timer = iom->addConditionTimer(timeout_ms, [winfo, fd, iom](){
                auto t = winfo.lock();
                if(!t || t->cancelled)
                {
                    return;
                }

                //超时，取消事件，回到HOLD（任务协程）
                t->cancelled = ETIMEDOUT;
                iom->cancelEvent(fd, johnsonli::IOManager::WRITE);
            }, winfo);
        }

        int rt = iom->addEvent(fd, johnsonli::IOManager::WRITE);
        if(rt == 0)
        {
            johnsonli::Fiber::YieldToHoldBySwap();
            //定时器还有，说明事件触发了，需要取消定时器
            if(timer)
            {
                // 此时取消定时器会强制执行定时任务，但是由于只是把定时任务加入到任务协程队列，因此不会马上执行。
                // 必须等退出connect_with_timeout，调度线程再去调度。但是此时weak_ptr已经被释放，条件不成立，定时任务最终不会被执行
                timer->cancel();    
            }
            if(tinfo->cancelled) {
                errno = tinfo->cancelled;
                return -1;
            }
        }
        else
        {
            //超时
            if(timer) {
                timer->cancel();
            }
            LOG_ERROR(g_logger) << "connect addEvent(" << fd << ", WRITE) error";
        }

        int error = 0;
        socklen_t len = sizeof(int);
        //检查有无错误
        if(-1 == getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len))
        {
            //调用失败
            return -1;
        }
        //没有错误
        if(!error)
        {
            return 0;
        }
        else    //有错误
        {
            errno = error;
            return -1;
        }

    }

    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) 
    {
        return connect_with_timeout(sockfd, addr, addrlen, johnsonli::s_connect_timeout);
    }

    int ioctl(int d, unsigned long int request, ...) 
    {
        va_list va;
        va_start(va, request);
        void* arg = va_arg(va, void*);
        va_end(va);

        if(FIONBIO == request) 
        {
            bool user_nonblock = !!*(int*)arg;
            johnsonli::FdCtx::ptr ctx = johnsonli::FdMgr::GetInstance()->get(d);
            if(!ctx || ctx->isClose() || !ctx->isSocket()) 
            {
                return ioctl_f(d, request, arg);
            }
            ctx->setUserNonblock(user_nonblock);
        }
        return ioctl_f(d, request, arg);
    }

    int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) 
    {
        return getsockopt_f(sockfd, level, optname, optval, optlen);
    }

    int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) 
    {
        if(!johnsonli::t_hook_enable) 
        {
            return setsockopt_f(sockfd, level, optname, optval, optlen);
        }
        if(level == SOL_SOCKET) 
        {
            if(optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) 
            {
                johnsonli::FdCtx::ptr ctx = johnsonli::FdMgr::GetInstance()->get(sockfd);
                if(ctx) 
                {
                    const timeval* v = (const timeval*)optval;
                    ctx->setTimeout(optname, v->tv_sec * 1000 + v->tv_usec / 1000);
                }
            }
        }
        return setsockopt_f(sockfd, level, optname, optval, optlen);
    }

}